package com.niiwoo.civet.trade.service.local.newRefund.refundDeal.core;

import com.niiwoo.civet.trade.constant.TradeMqConstant;
import com.niiwoo.civet.trade.dao.entity.RefundOrder;
import com.niiwoo.civet.trade.dao.entity.RefundOrderDetail;
import com.niiwoo.civet.trade.dao.entity.RefundOrderDetailBiz;
import com.niiwoo.civet.trade.dao.mapper.RefundOrderDetailBizMapperExt;
import com.niiwoo.civet.trade.dao.mapper.RefundOrderDetailMapperExt;
import com.niiwoo.civet.trade.dao.mapper.RefundOrderMapperExt;
import com.niiwoo.civet.trade.dto.newRefund.RefundOrderDetailDTO;
import com.niiwoo.civet.trade.enums.RefundOrderDetailStatusEnum;
import com.niiwoo.civet.trade.service.local.newRefund.callback.CallBackBorrowerRefundService;
import com.niiwoo.tripod.lanmao.component.LanMaoSequence;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 处理交易明细部分失败场景
 * Created by bincun@niiwoo.com on 2018/5/10.
 */
@Service
@Slf4j
public class RefundDetailHandlerService {
    @Autowired
    private RefundOrderMapperExt refundOrderMapperExt;
    @Autowired
    private RefundOrderDetailMapperExt refundOrderDetailMapperExt;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private LanMaoSequence lanMaoSequence;
    @Autowired
    private RefundOrderDetailBizMapperExt refundOrderDetailBizMapperExt;
    @Autowired
    private RefundToLanMaoService refundToLanMaoService;
    @Autowired
    private CallBackBorrowerRefundService callBackBorrowerRefundService;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 自动重发部分失败明细
     */
    public void processPartFailDetailOrderForJob(){
        //1 查询创时间超过10分钟，处于处理中的数据  status=1 ，计算本次还款金额
        List<RefundOrder> refundOrderList = refundOrderMapperExt.listForInProgress();
        for (RefundOrder refundOrder : refundOrderList) {
            if (new DateTime(refundOrder.getCreateTime()).plusMinutes(10).isAfter(new DateTime().getMillis())){
                log.info("订单创建不足10分钟，暂不处理,refundOrderId={}",refundOrder.getId());
                continue;
            }
            RefundDetailHandlerService refundDetailHandlerService = (RefundDetailHandlerService) AopContext.currentProxy();
            try {
                refundDetailHandlerService.handleOneRefundOrder(refundOrder.getId(),true);//如果部分失败，只重试一次
            }catch (Exception e){
                log.error("自动补单交易明细异常,refundOrderId={}",refundOrder.getId(),e);
            }
        }
    }

    /**
     * 处理还款订单状态
     * @param refundOrderId
     * @param oneRepeat 非JOB调用传false即可
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public void handleOneRefundOrder(Long refundOrderId,Boolean oneRepeat){
        log.info("交易明细处理,refundOrderId={}",refundOrderId);
        RefundOrder refundOrder = refundOrderMapperExt.selectByPrimaryKeyForUpdate(refundOrderId);
        Assert.notNull(refundOrder,"还款订单不存在,refundOrderId="+refundOrderId);
        this.doHandleOneRefundOrder(refundOrder,oneRepeat);
    }
    /**
     * @param refundOrder
     * @param oneRepeat 人工补单，次数不做限制，oneRepeat传false
     */
    private void doHandleOneRefundOrder(RefundOrder refundOrder,Boolean oneRepeat){
        if ( !Byte.valueOf("1").equals(refundOrder.getStatus()) ){
            log.info("还款订单交易状态非处理中,refundOrderId={}",refundOrder.getId());
            return;
        }
        Integer handingNum = refundOrderDetailMapperExt.countHandingDetail(refundOrder.getId());
        if ( handingNum > 0 ){
            log.info("还款订单交易明细正在处理中,fundOrderId={}",refundOrder.getId());
            return;
        }
        List<RefundOrderDetail> refundOrderDetails = refundOrderDetailMapperExt.selectByOrderIdNotDisable(refundOrder.getId());
        List<RefundOrderDetail> refundOrderDetailsFailed = new ArrayList<>();
        List<RefundOrderDetail> refundOrderDetailsSuccess = new ArrayList<>();

        for (RefundOrderDetail refundOrderDetail : refundOrderDetails) {
            if(Byte.valueOf("2").equals(refundOrderDetail.getStatus())){
                refundOrderDetailsSuccess.add(refundOrderDetail);
            }else if(Byte.valueOf("3").equals(refundOrderDetail.getStatus())){
                refundOrderDetailsFailed.add(refundOrderDetail);
            }
        }
        if(refundOrderDetailsFailed.size() == 0){// 全部成功
            callBackBorrowerRefundService.refundCallBackAllSuccess(refundOrder,refundOrderDetailsSuccess);
        } else if(refundOrderDetailsSuccess.size() == 0){// 全部失败
            log.info("明细全部失败，refundOrderId={}",refundOrder.getId());
            refundToLanMaoService.refundOrderHandleToFailed(refundOrder.getId(), refundOrder, new ArrayList<>(),null,null);
        } else{// 部分成功
            log.info("订单出现部分成功场景,refundOrderId={}",refundOrder.getId());
            this.handPartFailedRefund(refundOrder, refundOrderDetailsFailed, oneRepeat);
        }
    }


    private void handPartFailedRefund(RefundOrder refundOrder, List<RefundOrderDetail> refundOrderDetailsFailed, Boolean oneRepeat){
        if(oneRepeat){
            //自动还款只重发一次 oneRepeat=true
            int disabledCount = refundOrderDetailMapperExt.selectDisabledCount(refundOrder.getId());
            if(disabledCount > 0){
                return;
            }
        }
        String batchNo = lanMaoSequence.getBatchNo();
        List<RefundOrderDetailDTO> refundOrderDetailDTOs = new ArrayList<>();
        for (RefundOrderDetail refundOrderDetail : refundOrderDetailsFailed) {
            RefundOrderDetail refundOrderDetailUpdater = new RefundOrderDetail();
            refundOrderDetailUpdater.setId(refundOrderDetail.getId());
            refundOrderDetailUpdater.setStatus(RefundOrderDetailStatusEnum.DISABLED.getValue());
            refundOrderDetailUpdater.setUpdateTime(new Date());
            refundOrderDetailMapperExt.updateByPrimaryKeySelective(refundOrderDetailUpdater);

            RefundOrderDetailDTO refundOrderDetailDTO = new RefundOrderDetailDTO();
            BeanUtils.copyProperties(refundOrderDetail,refundOrderDetailDTO);
            refundOrderDetailDTO.setId(snowflakeIdWorker.nextId());
            refundOrderDetailDTO.setDetailRequestNo(lanMaoSequence.getRequestNo());
            refundOrderDetailDTO.setStatus(RefundOrderDetailStatusEnum.HANDLING.getValue());
            refundOrderDetailDTO.setBatchNo(batchNo);
            refundOrderDetailDTO.setCreateTime(new Date());
            refundOrderDetailDTO.setUpdateTime(new Date());
            refundOrderDetailMapperExt.insertSelective(refundOrderDetailDTO);

            List<RefundOrderDetailBiz> refundOrderDetailBizList = refundOrderDetailBizMapperExt.queryRefundOrderDetailBizByDetailRequestNo(refundOrderDetail.getDetailRequestNo());
            List<RefundOrderDetailBiz> refundOrderDetailBizNewList = new ArrayList<>();
            String newDetailsNo = refundOrderDetailDTO.getDetailRequestNo();

            //biz 业务明细直接更新即可
            for (RefundOrderDetailBiz biz : refundOrderDetailBizList) {
                RefundOrderDetailBiz bizNew = new RefundOrderDetailBiz();
                BeanUtils.copyProperties(biz,bizNew);
                bizNew.setId(snowflakeIdWorker.nextId());
                bizNew.setDetailRequestNo(newDetailsNo);
                bizNew.setBatchNo(batchNo);
                bizNew.setCreateTime(new Date());
                bizNew.setUpdateTime(new Date());

                refundOrderDetailBizMapperExt.insertSelective(bizNew);
                refundOrderDetailBizNewList.add(bizNew);
            }
            refundOrderDetailDTO.setRefundOrderDetailBizList(refundOrderDetailBizNewList);
            refundOrderDetailDTOs.add(refundOrderDetailDTO);
        }
        log.info("重发交易明细,原refundOrderId={},新生成BatchNo={}",refundOrder.getId(),batchNo);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                // 调起存管
                refundToLanMaoService.refundToLanMaoForRepeat(refundOrderDetailDTOs,batchNo,refundOrder.getId());
            }
        });
    }

    /**
     * 手动还款
     * @param refundOrderId
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
    public void handlePartFailedRefund(Long refundOrderId){
        RefundOrder refundOrder = refundOrderMapperExt.selectByPrimaryKeyForUpdate(refundOrderId);
        Assert.notNull(refundOrder,"还款订单不存在,refundOrderId="+refundOrderId);
        List<RefundOrderDetail> refundOrderDetails = refundOrderDetailMapperExt.selectByOrderIdNotDisable(refundOrder.getId());
        List<RefundOrderDetail> refundOrderDetailsFailed = new ArrayList<>();
        for (RefundOrderDetail refundOrderDetail : refundOrderDetails) {
            if(Byte.valueOf("3").equals(refundOrderDetail.getStatus())){
                refundOrderDetailsFailed.add(refundOrderDetail);
            }
        }
        this.handPartFailedRefund(refundOrder,refundOrderDetailsFailed,false);
    }
    /**
     *  订单明细终态处理统一服务
     *  非事物机制
     * @param refundOrderId
     */
    @Transactional( propagation = Propagation.NOT_SUPPORTED)
    public void refundDetailHandlerMQ(Long refundOrderId){
        if (refundOrderId != null) {
            rabbitTemplate.convertAndSend(TradeMqConstant.REFUND_ORDER_DETAIL_HANLDER_EXCHANGE, TradeMqConstant.REFUND_ORDER_DETAIL_HANLDER_ROUTING, refundOrderId);
        }else {
            log.info("订单对象不可空");
        }
    }
}
